﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using dw = System.Drawing;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace client.app.world
{
    public class Space : comm.world.BSpace, IBObject
    {
        public float OffsetX { get; set; }
        public float OffsetY { get; set; }
        public bool FirstView { get; set; }
        public LightMask LightMask { get; private set; }

        public int DrawSObjCount { get; private set; }
        public int MySObjCount { get { return _mySObjDic.Count; } }

        public LayerManager LayerManager { get; private set; }

        MainSpace _mainSpace = null;

        SpriteBatch _spriteBatch = null;
        Texture2D _texBg = null;
        Texture2D _texDot = null;
        Texture2D _texDot2 = null;
        SpriteFont _font = null;
        int[][] _mbDots = null;

        Rectangle _selectRect;
        bool _select = false;

        int[] _lastDown = null;
        string _lastTheaadId = "";
        bool _created = false;

        Dictionary<string, bool> _mySObjDic = new Dictionary<string, bool>();

        public Space(MainSpace mainSpace, comm.DefineFile defineFile, string mapId, string starId, string spaceId, Dictionary<string, comm.objst.DMap.Data.Obj> exObjDic = null)
            : base(defineFile, mapId, starId, spaceId, false, true, exObjDic)
        {
            _mainSpace = mainSpace;
            _spriteBatch = new SpriteBatch(MainGame.Instance.GraphicsDevice);
            _font = MainGame.Instance.Content.Load<SpriteFont>("font2");

            LayerManager = new LayerManager(comm.objst.DMap.Data.GetSpaceLayerTypes());

            #region draw dot2
            using (var bitmap = new dw.Bitmap(MapPathSize, MapPathSize))
            {
                bitmap.Draw(g =>
                {
                    g.DrawRectangle(dw.Color.Yellow.Pen(10), 0, 0, MapPathSize - 1, MapPathSize - 1);
                });
                bitmap.Save(ms =>
                {
                    _texDot2 = Texture2D.FromStream(MainGame.Instance.GraphicsDevice, ms);
                });
            }
            #endregion

            #region _texBg init
            using (var bitmap = new dw.Bitmap(MapWidth * MapPathSize, MapHeight * MapPathSize))
            {
                bitmap.Draw(g =>
                {
                    #region draw line
                    g.Clear(dw.Color.Gray);
                    var linePen = dw.Color.White.Pen(20);
                    var dotBrush = dw.Color.Black.SolidBrush(20);

                    for (int x = 1; x < MapWidth; x++)
                    {
                        g.DrawLine(linePen, x * MapPathSize, 0, x * MapPathSize, MapHeight * MapPathSize);
                    }

                    for (int y = 1; y < MapHeight; y++)
                    {
                        g.DrawLine(linePen, 0, y * MapPathSize, MapWidth * MapPathSize, y * MapPathSize);
                    }

                    for (int x = 0; x < MapWidth; x++)
                    {
                        for (int y = 0; y < MapHeight; y++)
                        {
                            if (LocMgr.GetPathType(x, y, 1).CanEnter())
                            {
                                g.FillRectangle(dotBrush, x * MapPathSize, y * MapPathSize, MapPathSize, MapPathSize);
                            }
                        }
                    }
                    #endregion
                });
                bitmap.Save(ms =>
                {
                    _texBg = Texture2D.FromStream(MainGame.Instance.GraphicsDevice, ms);
                });
            }
            #endregion

            #region _texDot
            using (var bitmap = new dw.Bitmap(20, 20))
            {
                bitmap.Draw(g =>
                {
                    g.DrawEllipse(dw.Color.Red.Pen(5), 0, 0, 20 - 1, 20 - 1);
                }, dw.Drawing2D.SmoothingMode.HighSpeed);
                bitmap.Save(ms =>
                {
                    _texDot = Texture2D.FromStream(MainGame.Instance.GraphicsDevice, ms);
                });
            }
            #endregion

            MainGame.Instance.AddTask(() =>
            {
                LightMask = new LightMask(this);
                _created = true;
            });
        }

        public void MouseRightClick(int x, int y)
        {
            if (_lastDown != null && _lastDown[0] == x && _lastDown[1] == y)
            {
                return;
            }
            _lastDown = new int[] { x, y };

            comm.ThreadHelper.Stop(_lastTheaadId);
            var begin = DateTime.Now;
            _lastTheaadId = comm.ThreadHelper.Run(() =>
            {
                var list = new List<SObject>();
                var list2 = new List<SObject>();
                foreach (var objectId in _mySObjDic.Keys)
                {
                    var bsobj = GetSObject(objectId);
                    if (bsobj != null)
                    {
                        var sobj = (SObject)bsobj;
                        if (sobj.PathSize == 1)
                        {
                            list.Add(sobj);
                        }
                        else
                        {
                            list2.Add(sobj);
                        }
                    }
                }
                #region sort by PathSize
                list2.Sort(new Comparison<SObject>((o1, o2) =>
                {
                    if (o1.PathSize > o2.PathSize)
                    {
                        return -1;
                    }
                    else if (o1.PathSize < o2.PathSize)
                    {
                        return 1;
                    }
                    return 0;
                }));
                //list.InsertRange(list.Count < 2 ? 0 : (int)Math.Floor(list.Count / 2d), list2.ToArray());
                list.InsertRange(0, list2.ToArray());
                #endregion

                if (list.Count == 1)
                {
                    var sobj = list[0];
                    var cLocX = (int)Math.Floor((x - OffsetX) / sobj.PathSize / MapPathSize);
                    var cLocY = (int)Math.Floor((y - OffsetY) / sobj.PathSize / MapPathSize);
                    sobj.BeginMove(cLocX, cLocY);
                }
                else if (list.Count > 1)
                {
                    var cLocX = (int)Math.Floor((x - OffsetX) / MapPathSize);
                    var cLocY = (int)Math.Floor((y - OffsetY) / MapPathSize);
                    #region loc
                    var info = DX.GetInfo(DXType.NORMAL, list.Count);
                    var dots = new List<int[]>();
                    var dic = new Dictionary<string, int[]>();
                    int index = -1, maxHeight = 0;

                    cLocX -= info.Width / 2;
                    cLocY -= info.Width / 2;

                    for (int iy = 0; iy < info.Height; iy++)
                    {
                        var lastDic = new Dictionary<string, int[]>();
                        for (int ix = 0; ix < info.Width; ix++)
                        {
                            if (info.Dots[ix, iy] == 1)
                            {
                                index++;
                                int bx = ix + cLocX, by = iy + cLocY;
                                var sobj = list[index];
                                int ox = bx, oy = by;
                                if (info.Has(ix - 1, iy))
                                {
                                    var left = dic[(ix - 1) + "," + iy];
                                    ox = left[0] + left[2];
                                    ox = (int)Math.Ceiling(ox / (double)sobj.PathSize) * sobj.PathSize;
                                }
                                else
                                {
                                    ox = (int)Math.Floor(ox / (double)sobj.PathSize) * sobj.PathSize;
                                }
                                if (maxHeight != 0)
                                {
                                    oy = maxHeight;
                                    oy = (int)Math.Ceiling(oy / (double)sobj.PathSize) * sobj.PathSize;
                                }
                                else
                                {
                                    oy = (int)Math.Floor(oy / (double)sobj.PathSize) * sobj.PathSize;
                                }
                                var arr = new int[] { ox, oy, sobj.PathSize };
                                dots.Add(arr);

                                lastDic.Add(ix + "," + iy, arr);
                                dic.Add(ix + "," + iy, arr);
                            }
                        }
                        var max = 0;
                        foreach (var key in lastDic.Keys)
                        {
                            var arr = lastDic[key];
                            max = Math.Max(max, arr[2] + arr[1]);
                        }
                        maxHeight = Math.Max(maxHeight, max);
                    }

                    _mbDots = dots.ToArray();

                    for (int i = 0, len = list.Count; i < len; i++)
                    {
                        var sobj = list[i];
                        if (sobj.MoveType == comm.objst.MoveType.FLIGHT)
                        {
                            var arr = dots[i];
                            sobj.BeginMove(arr[0] / sobj.PathSize, arr[1] / sobj.PathSize);
                        }
                    }
                    for (int i = 0, len = list.Count; i < len; i++)
                    {
                        var sobj = list[i];
                        if (sobj.MoveType != comm.objst.MoveType.FLIGHT)
                        {
                            var arr = dots[i];
                            sobj.BeginMove(arr[0] / sobj.PathSize, arr[1] / sobj.PathSize);
                        }
                    }
                    #endregion
                }
            });
            //Console.WriteLine("right click " + (DateTime.Now - begin).TotalMilliseconds);
        }
        public void SelectSObj(Rectangle rect)
        {
            return;
            _select = true;
            _selectRect = rect;
        }
        public bool SObjectIsSelect(string objectId)
        {
            return _mySObjDic.ContainsKey(objectId);
        }
        public SObject GetSObject(string objectId)
        {
            var sobj = GetBSObject(objectId);
            if (sobj is SObject)
            {
                return (SObject)sobj;
            }
            return null;
        }
        public SObject CreateSObject(comm.objst.SObjectInfo sobjInfo)
        {
            var sobj = new SObject(this, LocMgr, sobjInfo);
            if (AddBSObject(sobj))
            {
                if (sobj.PlayerId == MainGame.MainSpace.PlayerId)
                {
                    _mySObjDic.Add(sobj.ObjectId, true);
                }
                return sobj;
            }
            return null;
        }
        public int RemoveSObjects(string[] objectIds)
        {
            var count = 0;
            foreach (var objectId in objectIds)
            {
                count += RemoveBSObject(objectId) ? 1 : 0;
            }
            return count;
        }
        public void ClearAllSObject()
        {
            _mySObjDic.Clear();
            ClearAllBSObject();
        }

        protected override void OnUpdate(comm.TimeStat timeStat)
        {
            LayerManager.Update(timeStat);

            var begin = DateTime.Now;
            var allSObj = GetAllBSObject();
            var ms = (DateTime.Now - begin).TotalMilliseconds;
            if (ms > 0)
            {
                Console.WriteLine("update " + ms);
            }

            if (FirstView)
            {
                foreach (var objectId in _mySObjDic.Keys)
                {
                    var bsobj = GetSObject(objectId);
                    if (bsobj != null)
                    {
                        bsobj.Update(timeStat);
                    }
                }
            }

            var r = _selectRect;
            if (_select)
            {
                _mySObjDic.Clear();
            }
            foreach (var bsobj in allSObj)
            {
                var sobj = (SObject)bsobj;
                if (!(FirstView && _mySObjDic.ContainsKey(sobj.ObjectId)))
                {
                    sobj.Update(timeStat);
                }
                if (_select)
                {
                    if (sobj.PlayerId == MainGame.MainSpace.PlayerId && r.Contains((int)(sobj.DisplayX + OffsetX), (int)(sobj.DisplayY + OffsetY)) && _mySObjDic.Count < 12)
                    {
                        _mySObjDic.Add(sobj.ObjectId, true);
                    }
                }
            }
            _select = false;

            if (LightMask != null)
            {
                LightMask.Update(timeStat);
            }
        }

        public void BeginDraw(comm.TimeStat timeStat)
        {
            if (!_created)
            {
                return;
            }

            LayerManager.BeginDraw(timeStat);

            //LightMask.BeginDraw(timeStat);
        }
        public void Draw(comm.TimeStat timeStat)
        {
            if (!_created)
            {
                return;
            }

            _spriteBatch.Begin();
            _spriteBatch.Draw(_texBg, new Vector2(OffsetX, OffsetY), Color.White);

            #region mbDots
            if (_mbDots != null)
            {
                foreach (var dot in _mbDots)
                {
                    _spriteBatch.Draw(_texDot, new Vector2(
                        dot[0] * MapPathSize + dot[2] * MapPathSize / 2 - 10 + OffsetX,
                        dot[1] * MapPathSize + dot[2] * MapPathSize / 2 - 10 + OffsetY
                        ), Color.White);
                }
            }
            #endregion

            #region _pathDataDic
            //foreach (var size in LocMgr.PathSizes)
            //{
            var size = 1;
            var s = LocMgr.GetMapSize(size);
            int w = s[0], h = s[1];
            for (int x = 0; x < w; x++)
            {
                for (int y = 0; y < h; y++)
                {
                    if (!LocMgr.GetPathType(x, y, size).CanEnter())
                    {
                        _spriteBatch.Draw(_texDot2, new Rectangle(
                            (int)(x * size * MapPathSize + OffsetX),
                            (int)(y * size * MapPathSize + OffsetY),
                            size * MapPathSize,
                            size * MapPathSize), Color.White);
                    }
                }
            }
            //}
            #endregion

            _spriteBatch.End();

            var begin = DateTime.Now;
            var allSObj = GetAllBSObject();
            var ms = (DateTime.Now - begin).TotalMilliseconds;
            if (ms > 0)
            {
                Console.WriteLine("draw " + ms);
            }

            int drawCount = 0;
            foreach (var bsobj in allSObj)
            {
                var sobj = (SObject)bsobj;
                if (MainGame.ViewRect.Contains((int)(sobj.DisplayX + OffsetX), (int)(sobj.DisplayY + OffsetY)))
                {
                    drawCount++;
                    sobj.Draw(timeStat);
                }
            }
            DrawSObjCount = drawCount;

            LayerManager.Draw(timeStat);

            //LightMask.Draw(timeStat);

            _spriteBatch.Begin();

            #region block
            for (int bx = 0; bx < LocMgr.BlockWidth; bx++)
            {
                for (int by = 0; by < LocMgr.BlockHeight; by++)
                {
                    _spriteBatch.DrawString(_font, LocMgr.GetBlockObjectCount(bx, by).ToString(),
                        new Vector2(
                            bx * LocMgr.BlockSize * MapPathSize + OffsetX,
                            by * LocMgr.BlockSize * MapPathSize + OffsetY), Color.White);
                }
            }
            #endregion

            _spriteBatch.End();
        }

        public override string FindPath(int pathSize, int fromX, int fromY, int toX, int toY, Action<comm.world.FindTaskMode, comm.AStarNode[]> callback)
        {
            return _mainSpace.AddFindTask(pathSize, fromX, fromY, toX, toY, callback);
        }
        public override void CancelFindPath(string id)
        {
            _mainSpace.CancelFindTask(id);
        }
    }

}